package com.gremlin.utils;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Properties;

/**
 * Description: 邮件发送工具类
 * @author: gremlin
 * Date: 2022/3/4 10:06
 * @version: 1.0.0
 */
public class SendEmailUtils {
    /**
     * 发送邮件
     */
    public static void sendEmailConfig() throws MessagingException, UnsupportedEncodingException {
        // 1. 创建参数配置, 用于连接邮件服务器的参数配置
        Properties properties = new Properties();
        // 使用的协议（JavaMail规范要求）
        properties.setProperty("mail.transport.protocol", "smtp");
        // 发件人的邮箱的SMTP服务器地址
        properties.setProperty("mail.smtp.host", SenderInfoUtils.EMAIL_HOST);
        // 是否需要请求认证
        properties.setProperty("mail.smtp.auth", "true");
        properties.setProperty("mail.smtp.starttls.enable","true");

        /**
         * PS: 某些邮箱服务器要求 SMTP 连接需要使用 SSL
         * 安全认证 (为了提高安全性, 邮箱支持SSL连接, 也可以自己开启),
         * 如果无法连接邮件服务器, 仔细查看控制台打印的 log,
         * 如果有有类似 “连接失败, 要求 SSL 安全连接” 等错误,
         * 开启 SSL 安全连接。
         *
         * SMTP 服务器的端口 (非 SSL 连接的端口一般默认为 25, 可以不添加, 如果开启了 SSL 连接,
         * 需要改为对应邮箱的 SMTP 服务器的端口, 具体可查看对应邮箱服务的帮助,
         * QQ邮箱的SMTP(SLL)端口为465或587, 其他邮箱自行去查看)
         */
        final String smtpPort = "465";
        properties.setProperty("mail.smtp.port", smtpPort);
        properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        properties.setProperty("mail.smtp.socketFactory.fallback", "false");
        properties.setProperty("mail.smtp.socketFactory.port", smtpPort);

        // 2. 根据配置创建会话对象, 用于和邮件服务器交互
        Session session = Session.getInstance(properties
                , new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(SenderInfoUtils.EMAIL_ACCOUNT,SenderInfoUtils.EMAIL_PASSWORD);
                    }
        }
        );
        // 设置为debug模式, 可以查看详细的发送 log
        session.setDebug(true);

        // 收件人邮箱地址 可以有多个
        String recipientEmailAddress = "XXXXX@163.com";

        // 3. 调用私有方法创建一封邮件
        MimeMessage message = createMimeMessage(session,SenderInfoUtils.EMAIL_ACCOUNT, recipientEmailAddress);
        // MimeMessage message = complexEmail(session,SenderInfoUtils.EMAIL_ACCOUNT,recipientEmailAddress);

        // 4. 根据 Session 获取邮件传输对象
        Transport transport = session.getTransport();

        /**
         * 使用 邮箱账号 和 密码 连接邮件服务器, 这里认证的邮箱必须与 message 中的发件人邮箱一致, 否则报错
         *
         * PS_01: 如果连接服务器失败, 都会在控制台输出相应失败原因的log。
         *  仔细查看失败原因, 有些邮箱服务器会返回错误码或查看错误类型的链接,
         *  根据给出的错误类型到对应邮件服务器的帮助网站上查看具体失败原因。
         *
         * PS_02: 连接失败的原因通常为以下几点, 仔细检查代码:
         *  (1) 邮箱没有开启 SMTP 服务;
         *  (2) 邮箱密码错误, 例如某些邮箱开启了独立密码;
         *  (3) 邮箱服务器要求必须要使用 SSL 安全连接;
         *  (4) 请求过于频繁或其他原因, 被邮件服务器拒绝服务;
         *  (5) 如果以上几点都确定无误, 到邮件服务器网站查找帮助。
         */

        transport.connect(SenderInfoUtils.EMAIL_ACCOUNT, SenderInfoUtils.EMAIL_AUTH_CODE);

        // 6. 发送邮件, 发到所有的收件地址,
        // message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
        transport.sendMessage(message, message.getAllRecipients());

        // 7. 关闭连接
        transport.close();
    }

    /**
     * 创建一封邮件
     */
    private static MimeMessage createMimeMessage(Session session, String emailAccount,String recipientEmailAddress) throws UnsupportedEncodingException, MessagingException {
        // 1. 创建一封邮件
        MimeMessage message = new MimeMessage(session);
        // 2. From: 发件人（标题处显示）
        message.setFrom(new InternetAddress(emailAccount, "gremlin", "UTF-8"));

        /**
         * 3. To: 收件人（可以增加多个收件人、抄送、密送）
         * Message.RecipientType.TO：消息接受者 展示接收者
         * Message.RecipientType.CC：消息抄送者 展示抄送人
         * Message.RecipientType.BCC：匿名抄送接收者(其他接受者看不到这个接受者的姓名和地址) 不展示接收者和抄送人
         */
        //获取最后一个"@"的位置
        int index = recipientEmailAddress.lastIndexOf("@");
        //不含"@",这叫含头不含尾
        String recipientNickName = recipientEmailAddress.substring(0,index);
        message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(recipientEmailAddress,recipientNickName,"UTF-8"));

        // 4. Subject: 邮件主题
        message.setSubject("验证码", "UTF-8");

        // 可选 设置发送邮件已读回执
        message.addHeader("Disposition-Notification-To",emailAccount);

        // 5. Content: 邮件正文（可以使用html标签）
        int code = (int)(Math.random()*(9999-1000+1))+1000;
        String emailContent = "您的验证码为："+ code;
        message.setText(emailContent, "UTF-8");
        // 6. 设置发件时间
        message.setSentDate(new Date());
        // 7. 保存设置
        message.saveChanges();
        return message;
    }

    /**
     * Java实现文本图片附件复杂的邮件发送
     */
    private static MimeMessage complexEmail(Session session,String emailAccount,String recipientEmailAddress) throws UnsupportedEncodingException, MessagingException {
        // 1. 创建一封邮件
        MimeMessage message = new MimeMessage(session);
        // 2. From: 发件人（标题处显示）
        message.setFrom(new InternetAddress(emailAccount, "gremlin", "UTF-8"));

        // 3. To: 收件人（可以增加多个收件人、抄送、密送）
        /**
         * Message.RecipientType.TO：消息接受者 展示接受者
         * Message.RecipientType.CC：消息抄送者 展示抄送人
         * Message.RecipientType.BCC：匿名抄送接收者(其他接受者看不到这个接受者的姓名和地址) 不展示接收者和抄送人
         */
        message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(recipientEmailAddress,recipientEmailAddress.substring(0,recipientEmailAddress.lastIndexOf("@")),"UTF-8"));

        // 4. Subject: 邮件主题
        message.setSubject("复杂的邮件带有附件", "UTF-8");

        // 可选 设置发送邮件已读回执
        message.addHeader("Disposition-Notification-To",emailAccount);

        // 5. Content: 邮件正文
        //准备图片数据
        MimeBodyPart image = new MimeBodyPart();
        DataHandler handler = new DataHandler(
                new FileDataSource("D:/image/d932ca3f9b9e3e5c2c08e87a564b3e87.jpeg"));
        image.setDataHandler(handler);
        //设置图片id
        image.setContentID("test.png");

        //准备文本
        MimeBodyPart text = new MimeBodyPart();
        text.setContent("这是一段文本<img src='cid:test.png'>","text/html;charset=utf-8");

        //拼装邮件正文
        MimeMultipart mimeMultipart = new MimeMultipart();
        mimeMultipart.addBodyPart(image);
        mimeMultipart.addBodyPart(text);
        //文本和图片内嵌成功
        mimeMultipart.setSubType("related");

        //将拼装好的正文内容设置为主体
        MimeBodyPart contentText = new MimeBodyPart();
        contentText.setContent(mimeMultipart);

        //附件
        MimeBodyPart appendix = new MimeBodyPart();
        appendix.setDataHandler(
                new DataHandler(
                        new FileDataSource(
                                "D:/image/earth.mp4")));
        appendix.setFileName("earth.mp4");

        //拼接附件
        MimeMultipart allFile = new MimeMultipart();
        //附件
        allFile.addBodyPart(appendix);
        //正文
        allFile.addBodyPart(contentText);
        //正文和附件都存在邮件中，所有类型设置为mixed
        allFile.setSubType("mixed");

        //放到Message消息中
        message.setContent(allFile);

        // 6. 设置发件时间
        message.setSentDate(new Date());
        // 7. 保存设置
        message.saveChanges();
        return message;
    }
}
